package logic.moves;

import gamemngr.GameMngr;

import java.io.Serializable;
import java.util.Arrays;

import network.Client;

import logic.Color;
import logic.Country;
import logic.Dice;
import network.InformClients;
import logic.Player;

public class AttackMove implements GameMove, Serializable  {

	private static final long serialVersionUID = 1L;

	//=== Fields ===
	public static final String move_type = "ATTACKMOVE";

	private 	Country 	attackingCountry;		//the attacking country
	private		Country		defendingCountry;		//the attacked country

	private		Player		defender;
	private		Player		attacker;

	private		boolean		moved;

	private 	int		originalAttackingAmount;
	private		int		originalDefendingAmount;

	private		int		attackSoldiersLeft;
	private		int		defendSoldiersLeft;

	private		int[] 	defenderStrikes;
	private		int[] 	attackerStrikes;

	private boolean conquered;

	private int moveSoldiers;

	private boolean isNetwork;

	private GameMngr game;

	/**
	 * @param attackingCountry
	 * @param defendingCountry
	 * @param player
	 */
	public AttackMove(Country attackingCountry, Country defendingCountry) {
		this.attackingCountry = attackingCountry;
		this.defendingCountry = defendingCountry;
		attacker = attackingCountry.getOwner();
		defender = defendingCountry.getOwner();
		this.moved = false;
		this.originalAttackingAmount = attackingCountry.getSoldiers();
		this.originalDefendingAmount = defendingCountry.getSoldiers();
	}
	
	/**
	 * 
	 * @param attackingCountry
	 * @param defendingCountry
	 * @param moveSoldiers - For AI to be used as a pre-defined amount of soldiers to move
	 * 						  from attacking to defending in case of conquering.
	 */
	public AttackMove(Country attackingCountry, Country defendingCountry, int moveSoldiers)
	{
		this(attackingCountry, defendingCountry);
		this.moveSoldiers = moveSoldiers;
	}
	
	public AttackMove(Country attackingCountry, Country defendingCountry, boolean isNetwork, GameMngr gameMngr){
		this(attackingCountry, defendingCountry);
		this.isNetwork = isNetwork;
		this.game = gameMngr;
	}

	public boolean hasMoved() {
		return moved;
	}



	public int getAttackSoldiersLeft() {
		return attackSoldiersLeft;
	}

	public int getDefendSoldiersLeft() {
		return defendSoldiersLeft;
	}

	/**
	 * @return the attackingCountry
	 */
	public Country getAttackingCountry() {
		return attackingCountry;
	}

	/**
	 * @return the defendingCountry
	 */
	public Country getDefendingCountry() {
		return defendingCountry;
	}

	public boolean isLegalMove(){
		boolean rc = this.defendingCountry.getOwner() != null;
		rc = rc && this.attackingCountry.getOwner() != null;
		rc = rc && !this.attackingCountry.getOwner().equals(this.defendingCountry.getOwner());
		rc = rc && this.attackingCountry.getNeighbors().containsValue(this.defendingCountry);
		rc = rc && this.attackingCountry.getSoldiers() > 1;
		return rc;
	}

	/**
	 * perform one round of attack
	 */
	public boolean makeMove() {
		if(!this.hasMoved())
		{
			attackerStrikes = null;
			defenderStrikes = null;
			Dice redDie = new Dice(Color.RED);
			Dice blueDie = new Dice(Color.BLUE);

			int strike0 = redDie.roll();
			if(this.attackingCountry.getSoldiers() > 2){
				int strike1 = redDie.roll();
				if(this.attackingCountry.getSoldiers() > 3){
					int strike2 = redDie.roll();
					attackerStrikes = new int[3];
					attackerStrikes[0] = strike0;
					attackerStrikes[1] = strike1;
					attackerStrikes[2] = strike2;
				}else{
					attackerStrikes = new int[2];
					attackerStrikes[0] = strike0;
					attackerStrikes[1] = strike1;
				}
			}else{
				attackerStrikes = new int[1];
				attackerStrikes[0] = strike0;
			}

			int defend0 = blueDie.roll();
			if(this.defendingCountry.getSoldiers() > 1){
				int defend1 = blueDie.roll();
				defenderStrikes = new int[2];
				defenderStrikes[0] = defend0;
				defenderStrikes[1] = defend1;
			}else{
				defenderStrikes = new int[1];
				defenderStrikes[0] = defend0;
			}

			sortArrayDesc(defenderStrikes);
			sortArrayDesc(attackerStrikes);

			int n = Math.min(defenderStrikes.length, attackerStrikes.length);
			for(int i=0; i<n; i++){
				if(attackerStrikes[i] > defenderStrikes[i]){
					this.defendingCountry.killSoldier();//
				}else{
					this.attackingCountry.killSoldier();//
				}
			}

			attackSoldiersLeft = attackingCountry.getSoldiers();
			defendSoldiersLeft = defendingCountry.getSoldiers();

			if(isAttackWon())
			{
				conquered = true;
				defender.removeCountry(defendingCountry);
				attacker.addCountry(defendingCountry);
				// for Automatic Player (AI) will have an initial amount of soldiers to move
				// after a successful attack.
				if(attacker.isComputer())
				{
					moveSoldiers = Math.max(attackerStrikes.length, moveSoldiers);
					moveSoldiersOnConquer(isNetwork, game);
				}
				// this is when human player didn't set number of soldiers
				// to move in case of successful attack(moveSoldiers == 0) 
				else
					// actual moving is called from UI
					moveSoldiers = attackerStrikes.length;
			}
			this.moved = true;
		}
		return true;
	}

	public void sortArrayDesc(int[] arr){
		int n = arr.length;
		for(int i=0; i<n; i++){
			arr[i] *= -1; 
		}
		Arrays.sort(arr);
		for(int i=0; i<n; i++){
			arr[i] *= -1; 
		}
	}

	/**
	 * 
	 * @return true if attacker has lost war
	 */
	public boolean isAttackLost(){
		return attackSoldiersLeft <= 1;
	}

	/**
	 * 
	 * @return true if attacker has won war
	 */
	public boolean isAttackWon(){
		return defendSoldiersLeft <= 0;
	}

	public boolean isBattleFinished(){
		return (isAttackLost() || isAttackWon());
	}


	/**
	 * @return the defenderStrikes
	 */
	public int[] getDefenderStrikes() {
		return defenderStrikes;
	}

	/**
	 * @return the attackerStrikes
	 */
	public int[] getAttackerStrikes() {
		return attackerStrikes;
	}

	public void printDoMove() {
		System.out.println();
		System.out.println(attacker.getPlayerName() + " strikes: " + Arrays.toString(attackerStrikes));
		System.out.println(defender.getPlayerName() + " strikes: " + Arrays.toString(defenderStrikes));
		System.out.println(attacker.getPlayerName() + " in " + attackingCountry + ", has attacked " + defender.getPlayerName() + " in " + defendingCountry + ".");
		System.out.println(attacker.getPlayerName() + " has " + getAttackerCasualties() + " casualties.");
		System.out.println(attacker.getPlayerName() + " has " + attackSoldiersLeft + " Soldiers left.");

		System.out.println(defender.getPlayerName() + " has " + getDefenderCasualties() + " casualties.");
		System.out.println(defender.getPlayerName() + " has " + defendSoldiersLeft + " Soldiers left.");

		if(conquered)
		{
			System.out.println(attacker.getPlayerName() + " has conquered " + defendingCountry + "!");
		}
	}
	public void printUndoMove() {
		if(conquered)
		{
			System.out.println(attacker.getPlayerName() + " has un-conquered " + defendingCountry + ".");
		}
		System.out.println(this.attackingCountry.getOwner().getPlayerName() + " in " + this.attackingCountry + ", un-attacked " + this.defendingCountry.getOwner().getPlayerName() + " in " + this.defendingCountry);
	}
	/**
	 * @return number of soldiers which attacker lost during attack
	 */
	public int getAttackerCasualties(){
		return originalAttackingAmount - attackSoldiersLeft;
	}
	/**
	 * @return number of soldiers defender lost in the attack
	 */
	public int getDefenderCasualties(){
		return originalDefendingAmount - defendSoldiersLeft;
	}

	public void restore() {
		if(hasMoved()){
			if(conquered)
			{
				attacker.removeCountry(defendingCountry);
				defender.addCountry(defendingCountry);
			}
			defendingCountry.setSoldiersAmount(originalDefendingAmount);
			attackingCountry.setSoldiersAmount(originalAttackingAmount);
			moved = false;
		}
	}

	/**
	 * @return move type as string
	 */
	public String getType() {
		return move_type;
	}

	public int getMoveSoldiers() {
		return moveSoldiers;
	}
	
	public void setMoveSoldiers(int moveSoldiers)
	{
		this.moveSoldiers = moveSoldiers;
	}
	/**
	 * When conquered move soldiers from attacker to defender
	 * @param isNetwork TODO
	 * @param game TODO
	 */
	public void moveSoldiersOnConquer(boolean isNetwork, GameMngr game) {
		if(conquered)
		{
			attackingCountry.moveSoldiers(defendingCountry, moveSoldiers);
		}
	}

	
	public void printShortDescription() {
		System.out.println(attacker.getPlayerName() + " in " + attackingCountry + ", has attacked " + defender.getPlayerName() + " in " + defendingCountry + ".");
		if(conquered)
		{
			System.out.println(attacker.getPlayerName() + " has conquered " + defendingCountry + "!");
		}
		
	}

	public String getBriefDo() {
		String returnStr = null;
		if(conquered)
		{
			returnStr = attacker.getPlayerName() + " in " + attackingCountry + ", has attacked " + defender.getPlayerName() + " in " + defendingCountry + ".\r\n";
			returnStr += attacker.getPlayerName() + " has conquered " + defendingCountry + "!";
		} else {
			returnStr = attacker.getPlayerName() + " in " + attackingCountry + ", has attacked " + defender.getPlayerName() + " in " + defendingCountry + ".";
		}
		return returnStr;
	}

	public String getBriefUndo() {
		// TODO Auto-generated method stub
		return null;
	}
}
